/*     */ package com.zimbra.cs.mailbox;
/*     */ 
/*     */ import com.google.common.base.CharMatcher;
/*     */ import com.google.common.base.Joiner;
/*     */ import com.google.common.base.Splitter;
/*     */ import com.google.common.base.Strings;
/*     */ import com.google.common.collect.ImmutableList;
/*     */ import com.google.common.collect.Lists;
/*     */ import com.google.common.io.Closeables;
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.mailbox.ContactConstants;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.soap.Element;
/*     */ import com.zimbra.common.soap.SoapProtocol;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.GalContact;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.gal.GalGroup.GroupInfo;
/*     */ import com.zimbra.cs.gal.GalGroupInfoProvider;
/*     */ import com.zimbra.cs.gal.GalSearchControl;
/*     */ import com.zimbra.cs.gal.GalSearchParams;
/*     */ import com.zimbra.cs.gal.GalSearchResultCallback;
/*     */ import com.zimbra.cs.index.ContactHit;
/*     */ import com.zimbra.cs.index.ProxiedHit;
/*     */ import com.zimbra.cs.index.SearchParams;
/*     */ import com.zimbra.cs.index.SearchParams.Fetch;
/*     */ import com.zimbra.cs.index.SortBy;
/*     */ import com.zimbra.cs.index.ZimbraHit;
/*     */ import com.zimbra.cs.index.ZimbraQueryResults;
/*     */ import com.zimbra.cs.service.util.ItemId;
/*     */ import com.zimbra.soap.ZimbraSoapContext;
/*     */ import com.zimbra.soap.type.GalSearchType;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Collection;
/*     */ import java.util.Date;
/*     */ import java.util.EnumSet;
/*     */ import java.util.HashMap;
/*     */ import java.util.HashSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Set;
/*     */ import java.util.TreeSet;
/*     */ import java.util.regex.Matcher;
/*     */ import java.util.regex.Pattern;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class ContactAutoComplete
/*     */ {
/*     */   public static final class AutoCompleteResult
/*     */   {
/*     */     public ContactRankings rankings;
/*     */     public final Collection<ContactAutoComplete.ContactEntry> entries;
/*     */     public boolean canBeCached;
/*     */     public final int limit;
/*     */     private final Set<String> keys;
/*     */     
/*     */     public AutoCompleteResult(int l)
/*     */     {
/*  73 */       this.entries = new TreeSet();
/*  74 */       this.keys = new HashSet();
/*  75 */       this.canBeCached = true;
/*  76 */       this.limit = l;
/*     */     }
/*     */     
/*     */     public void addEntry(ContactAutoComplete.ContactEntry entry) {
/*  80 */       String key = entry.getKey();
/*  81 */       if (this.keys.contains(key)) {
/*  82 */         return;
/*     */       }
/*  84 */       if (this.entries.size() >= this.limit) {
/*  85 */         this.canBeCached = false;
/*  86 */         return;
/*     */       }
/*  88 */       if (entry.mRanking == 0)
/*     */       {
/*     */ 
/*     */ 
/*  92 */         int ranking = this.rankings.query(key);
/*  93 */         if (ranking > 0) {
/*  94 */           entry.mRanking = ranking;
/*     */         }
/*     */       }
/*  97 */       this.entries.add(entry);
/*  98 */       this.keys.add(key);
/*     */     }
/*     */     
/*     */     public void appendEntries(AutoCompleteResult result) {
/* 102 */       for (ContactAutoComplete.ContactEntry entry : result.entries) {
/* 103 */         addEntry(entry);
/*     */       }
/*     */     }
/*     */     
/*     */     void clear() {
/* 108 */       this.entries.clear();
/* 109 */       this.keys.clear();
/*     */     }
/*     */   }
/*     */   
/*     */   public static final class ContactEntry implements Comparable<ContactEntry>
/*     */   {
/*     */     String mEmail;
/*     */     String mDisplayName;
/*     */     String mLastName;
/*     */     boolean mIsContactGroup;
/*     */     boolean mIsGroup;
/*     */     boolean mCanExpandGroupMembers;
/*     */     ItemId mId;
/*     */     int mFolderId;
/*     */     int mRanking;
/*     */     long mLastAccessed;
/*     */     Map<String, ? extends Object> mAttrs;
/*     */     
/*     */     protected String getKey() {
/* 128 */       return (this.mIsContactGroup ? this.mDisplayName : this.mEmail).toLowerCase();
/*     */     }
/*     */     
/*     */     public String getEmail() {
/* 132 */       if (isContactGroup()) {
/* 133 */         return null;
/*     */       }
/* 135 */       StringBuilder buf = new StringBuilder();
/* 136 */       if ((this.mDisplayName != null) && (this.mDisplayName.length() > 0)) {
/* 137 */         buf.append("\"");
/* 138 */         buf.append(this.mDisplayName);
/* 139 */         buf.append("\" ");
/*     */       }
/* 141 */       buf.append("<").append(this.mEmail).append(">");
/* 142 */       return buf.toString();
/*     */     }
/*     */     
/*     */     public ItemId getId() {
/* 146 */       return this.mId;
/*     */     }
/*     */     
/*     */     public int getFolderId() {
/* 150 */       return this.mFolderId;
/*     */     }
/*     */     
/*     */     public int getRanking() {
/* 154 */       return this.mRanking;
/*     */     }
/*     */     
/*     */     public boolean isContactGroup() {
/* 158 */       return this.mIsContactGroup;
/*     */     }
/*     */     
/*     */     public boolean isGroup() {
/* 162 */       return this.mIsGroup;
/*     */     }
/*     */     
/*     */     public boolean canExpandGroupMembers() {
/* 166 */       return this.mCanExpandGroupMembers;
/*     */     }
/*     */     
/*     */     public String getDisplayName() {
/* 170 */       return this.mDisplayName;
/*     */     }
/*     */     
/*     */     public Map<String, ? extends Object> getAttrs() {
/* 174 */       return this.mAttrs;
/*     */     }
/*     */     
/*     */     void setIsGalGroup(String email, Map<String, ? extends Object> attrs, Account authedAcct, boolean needCanExpand) {
/* 178 */       setIsGalGroup(email, (String)attrs.get(ContactConstants.A_zimbraId), authedAcct, needCanExpand);
/*     */     }
/*     */     
/*     */     void setIsGalGroup(String email, String zimbraId, Account authedAcct, boolean needCanExpand) {
/* 182 */       boolean canExpand = false;
/* 183 */       if (needCanExpand) {
/* 184 */         canExpand = GalSearchControl.canExpandGalGroup(email, zimbraId, authedAcct);
/*     */       }
/* 186 */       setIsGalGroup(canExpand);
/*     */     }
/*     */     
/*     */     void setIsGalGroup(boolean canExpand) {
/* 190 */       this.mIsGroup = true;
/* 191 */       this.mCanExpandGroupMembers = canExpand;
/*     */     }
/*     */     
/*     */     void setIsContactGroup() {
/* 195 */       this.mIsGroup = true;
/* 196 */       this.mIsContactGroup = true;
/* 197 */       this.mCanExpandGroupMembers = true;
/*     */     }
/*     */     
/*     */     void setName(String name) {
/* 201 */       if (name == null) {
/* 202 */         name = "";
/*     */       }
/* 204 */       this.mDisplayName = name;
/* 205 */       this.mLastName = "";
/* 206 */       int space = name.lastIndexOf(' ');
/* 207 */       if (space > 0) {
/* 208 */         this.mLastName = name.substring(space + 1);
/*     */       }
/*     */     }
/*     */     
/*     */ 
/*     */     public int compareTo(ContactEntry that)
/*     */     {
/* 215 */       int nameCompare = getKey().compareToIgnoreCase(that.getKey());
/* 216 */       if (nameCompare == 0) {
/* 217 */         return 0;
/*     */       }
/*     */       
/* 220 */       int diff = that.mRanking - this.mRanking;
/* 221 */       if (diff != 0) {
/* 222 */         return diff;
/*     */       }
/*     */       
/*     */ 
/* 226 */       if (((this.mFolderId == 0) && (that.mFolderId != 0)) || (that.mFolderId == -1)) {
/* 227 */         return 1;
/*     */       }
/* 229 */       if (((this.mFolderId != 0) && (that.mFolderId == 0)) || (this.mFolderId == -1)) {
/* 230 */         return -1;
/*     */       }
/*     */       
/* 233 */       return nameCompare;
/*     */     }
/*     */     
/*     */     public boolean equals(Object obj)
/*     */     {
/* 238 */       if ((obj instanceof ContactEntry)) {
/* 239 */         return compareTo((ContactEntry)obj) == 0;
/*     */       }
/* 241 */       return false;
/*     */     }
/*     */     
/*     */     public String toString()
/*     */     {
/* 246 */       StringBuilder buf = new StringBuilder();
/* 247 */       toString(buf);
/* 248 */       return buf.toString();
/*     */     }
/*     */     
/*     */     public void toString(StringBuilder buf) {
/* 252 */       buf.append(this.mRanking).append(" ");
/* 253 */       if (isContactGroup()) {
/* 254 */         buf.append(getDisplayName()).append(" (contact group)");
/*     */       } else {
/* 256 */         buf.append(getEmail());
/*     */       }
/* 258 */       buf.append(" ").append(new Date(this.mLastAccessed));
/*     */     }
/*     */   }
/*     */   
/* 262 */   private static final Splitter TOKEN_SPLITTER = Splitter.on(CharMatcher.WHITESPACE).omitEmptyStrings().trimResults();
/*     */   
/*     */   public static final int FOLDER_ID_GAL = 0;
/*     */   
/*     */   public static final int FOLDER_ID_UNKNOWN = -1;
/*     */   public static final int FOLDER_ID_MOUNTPOINT_SUBFOLDER = -2;
/*     */   private boolean mIncludeGal;
/*     */   private boolean mNeedCanExpand;
/* 270 */   private static final Set<MailItem.Type> CONTACT_TYPES = EnumSet.of(MailItem.Type.CONTACT);
/*     */   
/*     */   private boolean mIncludeSharedFolders;
/*     */   
/*     */   private Collection<String> mEmailKeys;
/*     */   
/*     */   private GalSearchType mSearchType;
/*     */   private ZimbraSoapContext mZsc;
/*     */   private Account mAuthedAcct;
/*     */   private Account mRequestedAcct;
/*     */   private OperationContext octxt;
/*     */   private boolean returnFullContactData;
/* 282 */   private static final List<String> DEFAULT_EMAIL_KEYS = ImmutableList.of(ContactConstants.A_email, ContactConstants.A_email2, ContactConstants.A_email3);
/*     */   
/*     */   public ContactAutoComplete(Account acct, OperationContext octxt)
/*     */   {
/* 286 */     this(acct, null, octxt);
/*     */   }
/*     */   
/*     */   public ContactAutoComplete(Account acct, OperationContext octxt, boolean returnFullContactData) {
/* 290 */     this(acct, null, octxt, returnFullContactData);
/*     */   }
/*     */   
/*     */   public ContactAutoComplete(Account acct, ZimbraSoapContext zsc, OperationContext octxt) {
/* 294 */     this(acct, zsc, octxt, Boolean.FALSE.booleanValue());
/*     */   }
/*     */   
/*     */   public ContactAutoComplete(Account acct, ZimbraSoapContext zsc, OperationContext octxt, boolean returnFullContactData) {
/* 298 */     this.mZsc = zsc;
/* 299 */     this.octxt = octxt;
/* 300 */     this.returnFullContactData = returnFullContactData;
/*     */     try {
/* 302 */       this.mRequestedAcct = acct;
/* 303 */       this.mIncludeSharedFolders = this.mRequestedAcct.isPrefSharedAddrBookAutoCompleteEnabled();
/* 304 */       String contactEmailFields = this.mRequestedAcct.getContactEmailFields();
/* 305 */       if (contactEmailFields != null) {
/* 306 */         this.mEmailKeys = ImmutableList.copyOf(Splitter.on(',').split(contactEmailFields));
/*     */       }
/* 308 */       this.mIncludeGal = this.mRequestedAcct.isPrefGalAutoCompleteEnabled();
/*     */       
/* 310 */       if (this.mZsc != null) {
/* 311 */         String authedAcctId = this.mZsc.getAuthtokenAccountId();
/* 312 */         if (authedAcctId != null) {
/* 313 */           this.mAuthedAcct = Provisioning.getInstance().get(Key.AccountBy.id, authedAcctId);
/*     */         }
/*     */       }
/* 316 */       if (this.mAuthedAcct == null) {
/* 317 */         this.mAuthedAcct = this.mRequestedAcct;
/*     */       }
/*     */     } catch (ServiceException e) {
/* 320 */       ZimbraLog.gal.warn("error initializing ContactAutoComplete", e);
/*     */     }
/* 322 */     if (this.mEmailKeys == null) {
/* 323 */       this.mEmailKeys = DEFAULT_EMAIL_KEYS;
/*     */     }
/* 325 */     this.mSearchType = GalSearchType.account;
/*     */   }
/*     */   
/*     */   private String getRequestedAcctId() {
/* 329 */     return this.mRequestedAcct.getId();
/*     */   }
/*     */   
/*     */   public Collection<String> getEmailKeys() {
/* 333 */     return this.mEmailKeys;
/*     */   }
/*     */   
/*     */   public boolean includeGal() {
/* 337 */     return this.mIncludeGal;
/*     */   }
/*     */   
/*     */   public void setIncludeGal(boolean includeGal) {
/* 341 */     this.mIncludeGal = includeGal;
/*     */   }
/*     */   
/*     */   public void setNeedCanExpand(boolean needCanExpand) {
/* 345 */     this.mNeedCanExpand = needCanExpand;
/*     */   }
/*     */   
/*     */   public void setSearchType(GalSearchType type) {
/* 349 */     this.mSearchType = type;
/*     */   }
/*     */   
/*     */   public AutoCompleteResult query(String str, Collection<Integer> folders, int limit) throws ServiceException {
/* 353 */     ZimbraLog.gal.debug("AutoComplete querying: %s", new Object[] { str });
/* 354 */     long t0 = System.currentTimeMillis();
/* 355 */     AutoCompleteResult result = new AutoCompleteResult(limit);
/* 356 */     result.rankings = new ContactRankings(getRequestedAcctId());
/* 357 */     if (limit <= 0) {
/* 358 */       return result;
/*     */     }
/*     */     
/* 361 */     if (result.entries.size() >= limit) {
/* 362 */       return result;
/*     */     }
/*     */     
/*     */ 
/* 366 */     Collection<ContactEntry> rankingTableMatches = result.rankings.search(str);
/*     */     
/* 368 */     if (!rankingTableMatches.isEmpty()) {
/* 369 */       for (ContactEntry entry : rankingTableMatches) {
/* 370 */         String emailAddr = entry.getKey();
/* 371 */         resolveGroupInfo(entry, emailAddr);
/* 372 */         result.addEntry(entry);
/*     */       }
/*     */     }
/*     */     
/* 376 */     long t1 = System.currentTimeMillis();
/*     */     
/*     */ 
/* 379 */     if (result.entries.size() < limit) {
/* 380 */       queryFolders(str, folders, limit, result);
/*     */     }
/* 382 */     long t2 = System.currentTimeMillis();
/*     */     
/* 384 */     if ((this.mIncludeGal) && (result.entries.size() < limit)) {
/* 385 */       queryGal(str, result);
/*     */     }
/*     */     
/* 388 */     long t3 = System.currentTimeMillis();
/*     */     
/* 390 */     ZimbraLog.gal.info("autocomplete: overall=%dms, ranking=%dms, folder=%dms, gal=%dms", new Object[] { Long.valueOf(t3 - t0), Long.valueOf(t1 - t0), Long.valueOf(t2 - t1), Long.valueOf(t3 - t2) });
/*     */     
/* 392 */     return result;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */   private void resolveGroupInfo(ContactEntry entry, String email)
/*     */   {
/* 408 */     GalGroup.GroupInfo groupInfo = GalGroupInfoProvider.getInstance().getGroupInfo(email, this.mNeedCanExpand, this.mRequestedAcct, this.mAuthedAcct);
/*     */     
/* 410 */     if (groupInfo != null) {
/* 411 */       boolean canExpand = GalGroup.GroupInfo.CAN_EXPAND == groupInfo;
/* 412 */       entry.setIsGalGroup(canExpand);
/*     */       
/*     */ 
/* 415 */       entry.mFolderId = 0;
/*     */     }
/*     */   }
/*     */   
/*     */   private void queryGal(String str, AutoCompleteResult result) {
/* 420 */     ZimbraLog.gal.debug("querying gal");
/* 421 */     GalSearchParams params = new GalSearchParams(this.mRequestedAcct, this.mZsc);
/* 422 */     params.setQuery(str);
/* 423 */     params.setType(this.mSearchType);
/* 424 */     params.setLimit(200);
/* 425 */     params.setNeedCanExpand(this.mNeedCanExpand);
/* 426 */     params.setResultCallback(new AutoCompleteCallback(str, result, params));
/*     */     try {
/*     */       try {
/* 429 */         GalSearchControl gal = new GalSearchControl(params);
/* 430 */         gal.autocomplete();
/*     */       } catch (ServiceException e) {
/* 432 */         if ("service.PERM_DENIED".equals(e.getCode())) {
/* 433 */           ZimbraLog.gal.debug("cannot autocomplete gal: %s", new Object[] { e.getMessage() });
/*     */         } else {
/* 435 */           throw e;
/*     */         }
/*     */       }
/*     */     } catch (Exception e) {
/* 439 */       ZimbraLog.gal.warn("cannot autocomplete gal", e);
/* 440 */       return;
/*     */     }
/*     */   }
/*     */   
/*     */   private class AutoCompleteCallback extends GalSearchResultCallback {
/*     */     ContactAutoComplete.AutoCompleteResult result;
/*     */     String str;
/*     */     
/*     */     public AutoCompleteCallback(String str, ContactAutoComplete.AutoCompleteResult result, GalSearchParams params) {
/* 449 */       super();
/* 450 */       this.result = result;
/* 451 */       this.str = str;
/*     */     }
/*     */     
/*     */     public void handleContactAttrs(Map<String, ? extends Object> attrs) {
/* 455 */       ContactAutoComplete.this.addMatchedContacts(this.str, attrs, 0, null, this.result);
/*     */     }
/*     */     
/*     */     public Element handleContact(Contact c) throws ServiceException
/*     */     {
/* 460 */       ZimbraLog.gal.debug("gal entry: %d", new Object[] { Integer.valueOf(c.getId()) });
/* 461 */       handleContactAttrs(c.getFields());
/* 462 */       return null;
/*     */     }
/*     */     
/*     */     public void visit(GalContact c) throws ServiceException
/*     */     {
/* 467 */       ZimbraLog.gal.debug("gal entry: %s", new Object[] { c.getId() });
/* 468 */       handleContactAttrs(c.getAttrs());
/*     */     }
/*     */     
/*     */     public void handleElement(Element e) throws ServiceException
/*     */     {
/* 473 */       ZimbraLog.gal.debug("gal entry: %s", new Object[] { e.getAttribute("id") });
/* 474 */       handleContactAttrs(parseContactElement(e));
/*     */     }
/*     */     
/*     */ 
/*     */ 
/*     */     public void setSortBy(String sortBy) {}
/*     */     
/*     */ 
/*     */     public void setQueryOffset(int offset) {}
/*     */     
/*     */ 
/*     */     public void setHasMoreResult(boolean more) {}
/*     */   }
/*     */   
/*     */ 
/*     */   private String getFieldAsString(Map<String, ? extends Object> attrs, String fieldName)
/*     */   {
/* 491 */     Object value = attrs.get(fieldName);
/* 492 */     if ((value instanceof String))
/* 493 */       return (String)value;
/* 494 */     if ((value instanceof String[])) {
/* 495 */       String[] values = (String[])value;
/* 496 */       if (values.length > 0) {
/* 497 */         return values[0];
/*     */       }
/*     */     }
/* 500 */     return null;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   protected void addEntry(ContactEntry entry, AutoCompleteResult result)
/*     */   {
/* 508 */     result.addEntry(entry);
/*     */   }
/*     */   
/*     */   private boolean matchesEmail(List<String> tokens, String email) {
/* 512 */     if ((!Strings.isNullOrEmpty(email)) && (tokens.size() == 1)) {
/* 513 */       String token = (String)tokens.get(0);
/* 514 */       return email.toLowerCase().startsWith(token);
/*     */     }
/*     */     
/* 517 */     return false;
/*     */   }
/*     */   
/*     */   private boolean matchesName(List<String> tokens, String name) {
/* 521 */     if ((tokens.isEmpty()) || (Strings.isNullOrEmpty(name)))
/* 522 */       return false;
/* 523 */     if (tokens.size() == 1) {
/* 524 */       return name.toLowerCase().startsWith((String)tokens.get(0));
/*     */     }
/* 526 */     return toPattern(tokens).matcher(name).matches();
/*     */   }
/*     */   
/*     */   private boolean matchesName(List<String> tokens, Map<String, ? extends Object> attrs)
/*     */   {
/* 531 */     if (tokens.isEmpty())
/* 532 */       return false;
/* 533 */     if (tokens.size() == 1) {
/* 534 */       String token = (String)tokens.get(0);
/* 535 */       String firstName = getFieldAsString(attrs, ContactConstants.A_firstName);
/* 536 */       if ((!Strings.isNullOrEmpty(firstName)) && (firstName.toLowerCase().startsWith(token))) {
/* 537 */         return true;
/*     */       }
/* 539 */       String lastName = getFieldAsString(attrs, ContactConstants.A_lastName);
/* 540 */       if ((!Strings.isNullOrEmpty(lastName)) && (lastName.toLowerCase().startsWith(token))) {
/* 541 */         return true;
/*     */       }
/* 543 */       String middleName = getFieldAsString(attrs, ContactConstants.A_middleName);
/* 544 */       if ((!Strings.isNullOrEmpty(middleName)) && (middleName.toLowerCase().startsWith(token))) {
/* 545 */         return true;
/*     */       }
/* 547 */       String fullName = getFieldAsString(attrs, ContactConstants.A_fullName);
/* 548 */       if (!Strings.isNullOrEmpty(fullName)) {
/* 549 */         for (String fullNameToken : TOKEN_SPLITTER.split(fullName)) {
/* 550 */           if ((!Strings.isNullOrEmpty(fullNameToken)) && (fullNameToken.toLowerCase().startsWith(token)))
/*     */           {
/* 552 */             return true;
/*     */           }
/*     */         }
/*     */       }
/* 556 */       String nickname = getFieldAsString(attrs, ContactConstants.A_nickname);
/* 557 */       if ((!Strings.isNullOrEmpty(nickname)) && (nickname.toLowerCase().startsWith(token))) {
/* 558 */         return true;
/*     */       }
/*     */       
/* 561 */       String phoneticLastName = getFieldAsString(attrs, ContactConstants.A_phoneticLastName);
/* 562 */       if ((!Strings.isNullOrEmpty(phoneticLastName)) && (phoneticLastName.toLowerCase().startsWith(token))) {
/* 563 */         return true;
/*     */       }
/* 565 */       String phoneticFirstName = getFieldAsString(attrs, ContactConstants.A_phoneticFirstName);
/* 566 */       if ((!Strings.isNullOrEmpty(phoneticFirstName)) && (phoneticFirstName.toLowerCase().startsWith(token))) {
/* 567 */         return true;
/*     */       }
/* 569 */       return false;
/*     */     }
/* 571 */     Pattern pattern = toPattern(tokens);
/*     */     
/* 573 */     String firstName = getFieldAsString(attrs, ContactConstants.A_firstName);
/* 574 */     String lastName = getFieldAsString(attrs, ContactConstants.A_lastName);
/* 575 */     String middleName = getFieldAsString(attrs, ContactConstants.A_middleName);
/*     */     
/* 577 */     if (pattern.matcher(Joiner.on(' ').skipNulls().join(firstName, middleName, new Object[] { lastName })).matches()) {
/* 578 */       return true;
/*     */     }
/*     */     
/* 581 */     if (pattern.matcher(Joiner.on(' ').skipNulls().join(lastName, firstName, new Object[] { middleName })).matches()) {
/* 582 */       return true;
/*     */     }
/*     */     
/*     */ 
/* 586 */     String fullName = getFieldAsString(attrs, ContactConstants.A_fullName);
/* 587 */     if ((!Strings.isNullOrEmpty(fullName)) && (pattern.matcher(fullName).matches())) {
/* 588 */       return true;
/*     */     }
/*     */     
/* 591 */     String nickname = getFieldAsString(attrs, ContactConstants.A_nickname);
/* 592 */     if ((!Strings.isNullOrEmpty(nickname)) && (pattern.matcher(nickname).matches())) {
/* 593 */       return true;
/*     */     }
/*     */     
/* 596 */     String phoneticFirstName = getFieldAsString(attrs, ContactConstants.A_phoneticFirstName);
/* 597 */     String phoneticLastName = getFieldAsString(attrs, ContactConstants.A_phoneticLastName);
/*     */     
/* 599 */     if (pattern.matcher(Joiner.on(' ').skipNulls().join(phoneticLastName, phoneticFirstName, new Object[0])).matches()) {
/* 600 */       return true;
/*     */     }
/*     */     
/* 603 */     if (pattern.matcher(Joiner.on(' ').skipNulls().join(phoneticFirstName, phoneticLastName, new Object[0])).matches()) {
/* 604 */       return true;
/*     */     }
/* 606 */     return false;
/*     */   }
/*     */   
/*     */   private Pattern toPattern(List<String> tokens)
/*     */   {
/* 611 */     StringBuilder regex = new StringBuilder();
/* 612 */     for (String token : tokens) {
/* 613 */       regex.append(regex.length() == 0 ? "(^|.*\\s)" : "\\s").append(Pattern.quote(token)).append(".*");
/*     */     }
/* 615 */     return Pattern.compile(regex.toString(), 66);
/*     */   }
/*     */   
/*     */   public void addMatchedContacts(String query, Map<String, ? extends Object> attrs, int folderId, ItemId id, AutoCompleteResult result)
/*     */   {
/* 620 */     if (!result.canBeCached) {
/* 621 */       return;
/*     */     }
/*     */     
/* 624 */     List<String> tokens = Lists.newArrayListWithExpectedSize(1);
/* 625 */     for (String token : TOKEN_SPLITTER.split(query))
/* 626 */       tokens.add(token.toLowerCase());
/*     */     boolean nameMatches;
/*     */     String fullName;
/* 629 */     if ((!Contact.isGroup(attrs)) || (folderId == 0))
/*     */     {
/*     */ 
/*     */ 
/*     */ 
/* 634 */       nameMatches = matchesName(tokens, attrs);
/*     */       
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 646 */       fullName = getFieldAsString(attrs, ContactConstants.A_fullName);
/* 647 */       if (Strings.isNullOrEmpty(fullName)) {
/* 648 */         String first = getFieldAsString(attrs, ContactConstants.A_firstName);
/* 649 */         String middle = getFieldAsString(attrs, ContactConstants.A_middleName);
/* 650 */         String last = getFieldAsString(attrs, ContactConstants.A_lastName);
/* 651 */         fullName = Joiner.on(' ').skipNulls().join(first, middle, new Object[] { last });
/*     */       }
/*     */       
/* 654 */       for (String emailKey : this.mEmailKeys) {
/* 655 */         String email = getFieldAsString(attrs, emailKey);
/* 656 */         if ((email != null) && ((nameMatches) || (matchesEmail(tokens, email)))) {
/* 657 */           ContactEntry entry = new ContactEntry();
/* 658 */           entry.mEmail = email;
/* 659 */           entry.setName(fullName);
/* 660 */           entry.mId = id;
/* 661 */           entry.mFolderId = folderId;
/* 662 */           if (Contact.isGroup(attrs)) {
/* 663 */             entry.setIsGalGroup(email, attrs, this.mAuthedAcct, this.mNeedCanExpand);
/* 664 */           } else if (entry.mFolderId != 0)
/*     */           {
/*     */ 
/* 667 */             resolveGroupInfo(entry, email);
/*     */           }
/*     */           
/* 670 */           if (this.returnFullContactData) {
/* 671 */             entry.mAttrs = attrs;
/*     */           }
/*     */           
/* 674 */           addEntry(entry, result);
/* 675 */           ZimbraLog.gal.debug("adding %s", new Object[] { entry.getEmail() });
/* 676 */           if (folderId == 0)
/*     */           {
/*     */ 
/* 679 */             return;
/*     */           }
/*     */         }
/*     */       }
/*     */     } else {
/* 684 */       String nickname = getFieldAsString(attrs, ContactConstants.A_nickname);
/* 685 */       if (matchesName(tokens, nickname)) {
/* 686 */         ContactEntry entry = new ContactEntry();
/* 687 */         entry.mDisplayName = nickname;
/* 688 */         entry.mId = id;
/* 689 */         entry.mFolderId = folderId;
/* 690 */         entry.setIsContactGroup();
/* 691 */         result.addEntry(entry);
/* 692 */         ZimbraLog.gal.debug("adding %s", new Object[] { entry.getKey() });
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void queryFolders(String str, Collection<Integer> folderIDs, int limit, AutoCompleteResult result) throws ServiceException {
/* 698 */     str = str.toLowerCase();
/* 699 */     ZimbraQueryResults qres = null;
/*     */     try {
/* 701 */       Mailbox mbox = MailboxManager.getInstance().getMailboxByAccountId(getRequestedAcctId());
/* 702 */       List<Folder> folders = new ArrayList();
/* 703 */       Map<ItemId, Mountpoint> mountpoints = new HashMap();
/* 704 */       Iterator i$; if (folderIDs == null) {
/* 705 */         for (Folder folder : mbox.getFolderList(this.octxt, SortBy.NONE)) {
/* 706 */           if ((folder.getDefaultView() == MailItem.Type.CONTACT) && (!folder.inTrash()))
/*     */           {
/* 708 */             if ((folder instanceof Mountpoint)) {
/* 709 */               Mountpoint mp = (Mountpoint)folder;
/* 710 */               mountpoints.put(mp.getTarget(), mp);
/* 711 */               if (this.mIncludeSharedFolders) {
/* 712 */                 folders.add(folder);
/*     */               }
/*     */             } else {
/* 715 */               folders.add(folder);
/*     */             } }
/*     */         }
/*     */       } else {
/* 719 */         for (i$ = folderIDs.iterator(); i$.hasNext();) { int fid = ((Integer)i$.next()).intValue();
/* 720 */           Folder folder = mbox.getFolderById(this.octxt, fid);
/* 721 */           folders.add(folder);
/* 722 */           if ((folder instanceof Mountpoint)) {
/* 723 */             Mountpoint mp = (Mountpoint)folder;
/* 724 */             mountpoints.put(mp.getTarget(), mp);
/*     */           }
/*     */         }
/*     */       }
/* 728 */       SearchParams params = new SearchParams();
/* 729 */       params.setQueryString(generateQuery(str, folders));
/* 730 */       params.setDefaultField("contact:");
/* 731 */       params.setTypes(CONTACT_TYPES);
/* 732 */       params.setSortBy(SortBy.NONE);
/* 733 */       params.setLimit(limit + 1);
/* 734 */       params.setPrefetch(true);
/* 735 */       params.setFetchMode(SearchParams.Fetch.NORMAL);
/* 736 */       ZimbraLog.gal.debug("querying contact folders: %s", new Object[] { params.getQueryString() });
/* 737 */       qres = mbox.index.search(SoapProtocol.Soap12, this.octxt, params);
/* 738 */       while (qres.hasNext()) {
/* 739 */         ZimbraHit hit = qres.getNext();
/* 740 */         Map<String, String> fields = null;
/* 741 */         ItemId id = null;
/* 742 */         int fid = 0;
/* 743 */         if ((hit instanceof ContactHit)) {
/* 744 */           Contact c = ((ContactHit)hit).getContact();
/* 745 */           ZimbraLog.gal.debug("hit: %d", new Object[] { Integer.valueOf(c.getId()) });
/* 746 */           fields = c.getFields();
/* 747 */           id = new ItemId(c);
/* 748 */           fid = c.getFolderId();
/* 749 */           if (this.returnFullContactData) {
/* 750 */             List<Contact.Attachment> contactAttachments = c.getAttachments();
/* 751 */             if ((contactAttachments != null) && (contactAttachments.size() != 0))
/* 752 */               fields.put("image", c.getId() + "_" + ((Contact.Attachment)contactAttachments.get(0)).getName());
/*     */           }
/*     */         } else {
/* 755 */           if (!(hit instanceof ProxiedHit)) continue;
/* 756 */           fields = new HashMap();
/* 757 */           Element top = ((ProxiedHit)hit).getElement();
/* 758 */           id = new ItemId(top.getAttribute("id"), (String)null);
/* 759 */           ZimbraLog.gal.debug("hit: %s", new Object[] { id });
/* 760 */           ItemId fiid = new ItemId(top.getAttribute("l"), (String)null);
/* 761 */           Mountpoint mp = (Mountpoint)mountpoints.get(fiid);
/* 762 */           if (mp != null)
/*     */           {
/*     */ 
/* 765 */             fid = mp.getId();
/*     */           } else {
/* 767 */             fid = -2;
/*     */           }
/* 769 */           for (Element elt : top.listElements("a")) {
/*     */             try {
/* 771 */               String name = elt.getAttribute("n");
/* 772 */               fields.put(name, elt.getText());
/*     */             } catch (ServiceException se) {
/* 774 */               ZimbraLog.gal.warn("error handling proxied query result " + hit);
/*     */             }
/*     */           }
/* 777 */           if ((this.returnFullContactData) && 
/* 778 */             (fields.containsKey("image"))) {
/* 779 */             fields.put("image", id.getAccountId() + "_" + id.getId() + "_image");
/*     */           }
/*     */         }
/*     */         
/*     */ 
/*     */ 
/*     */ 
/* 786 */         addMatchedContacts(str, fields, fid, id, result);
/* 787 */         if (!result.canBeCached) {
/*     */           return;
/*     */         }
/*     */       }
/*     */     } finally {
/* 792 */       Closeables.closeQuietly(qres);
/*     */     }
/*     */   }
/*     */   
/*     */   private String generateQuery(String query, Collection<Folder> folders) {
/* 797 */     StringBuilder buf = new StringBuilder("(");
/* 798 */     boolean first = true;
/* 799 */     for (Folder folder : folders) {
/* 800 */       int fid = folder.getId();
/* 801 */       if (fid >= 1)
/*     */       {
/*     */ 
/* 804 */         if (first) {
/* 805 */           first = false;
/*     */         } else {
/* 807 */           buf.append(" OR ");
/*     */         }
/*     */         
/* 810 */         buf.append((folder instanceof Mountpoint) ? "underid:" : "inid:");
/* 811 */         buf.append(fid);
/*     */       } }
/* 813 */     buf.append(')');
/* 814 */     for (String token : TOKEN_SPLITTER.split(query)) {
/* 815 */       buf.append(" \"").append(token.replace("\"", "\\\"")).append('"');
/*     */     }
/* 817 */     return buf.toString();
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/mailbox/ContactAutoComplete.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */